library POVTree;

uses
  Winapi.Windows,
  System.SysUtils,
  System.UITypes,
  System.Classes,
  Vcl.Forms,
  System.Win.Registry,
  Winapi.ShellApi,
  Vcl.Dialogs,
  GLS.VectorTypes,
  fPovProgress in 'fPovProgress.pas' {ProgressForm},
  uGlsVector in 'uGlsVector.pas',
  fPovMain in 'fPovMain.pas' {MainForm};

{$R *.res}

type PLeaf = ^TLeaf;
     TLeaf = record
       RotationX: real;
       RotationY: real;
       RotationZ: real;
       Origin: TPoint3d;
     end;

type PFruit = ^TFruit;
     TFruit = record
       RotationX: real;
       RotationY: real;
       RotationZ: real;
       Origin: TPoint3d;
     end;

var f: TextFile;    {used a lot for many files}
    IncludeAxes: boolean;
    NormalizeTreeDimensions: boolean;
    POVRayExecutable: string;
    POVRayCommandLine: string;
    POVRayInputFileName: string;
    ImageFileName: string;
    ImageWidth: integer;
    ImageHeight: integer;
    ImageFormat: integer;
    ImageBColor: integer;
    Rotation : integer;
    Animation: integer;
    ImageTotal: integer;
    TotalLeaves: integer;
    TotalFruit: integer;
    TotalStems: integer;
    TotalVertices: integer;
    TotalFaces: integer;
    ProgressForm: TProgressForm;

function ShowForm: integer; stdcall;
var Form: TMainForm;
begin
  Form := TMainForm.Create(Application);
  Result := Form.ShowModal;
  Form.Free;
end;

function Initialize(MaxX, MaxY, MaxZ: real; ATotalStems: integer): integer; stdcall;
var
 InString:String;
begin

  ProgressForm := TProgressForm.Create(Application);
  ProgressForm.ProgressBar.Max := ATotalStems{+Leaf +Fruit}- 1;
  ProgressForm.Show;
  { TotalVertices and TotalFaces hold the total amount of vertices and faces
    in the entire tree }

  TotalVertices := 0;
  TotalFaces := 0;
  TotalFruit:= 0;
  TotalLeaves:= 0;
  { TotalStems is used for the internal counting of the total number of stems,
    ATotalStems is used to initialize the progress bar of the progress form }

  TotalStems := 0;

  FormatSettings.DecimalSeparator := '.';
  FormatSettings.ThousandSeparator := #0;
   if FileExists(ExtractFilePath(ParamStr(0)) + 'PovTreal.ini') then
   begin
  { Read the settings from the Treal.ini }
  try
  AssignFile(f, ExtractFilePath(ParamStr(0)) + 'PovTreal.ini');
  try
    Reset(f);
    Readln(f,InString);
    If InString='T' then IncludeAxes :=True else IncludeAxes :=False;
    Readln(f,InString);
    If InString='T' then NormalizeTreeDimensions :=True else NormalizeTreeDimensions :=False;
    Readln(f,InString);  POVRayExecutable := InString;
    Readln(f,InString);  POVRayCommandLine := InString;
    Readln(f,InString);  POVRayInputFileName := InString;
    Readln(f,InString);  ImageFileName := InString;
    Readln(f,InString);  ImageFormat := StrtoInt(InString);
    Readln(f,InString);  ImageWidth := StrtoInt(InString);
    Readln(f,InString);  ImageHeight := StrtoInt(InString);
    Readln(f,InString);  ImageBColor := StrtoInt(InString);
    Readln(f,InString);  Rotation := StrtoInt(InString);
    Readln(f,InString);  Animation := StrtoInt(InString);
    Readln(f,InString);  ImageTotal := StrtoInt(InString);
    {3 Path lines are not needed Here}
     except
      on EInOutError do
      begin
      MessageDlg('POVTReal should be configured first!', mtError, [mbOK], 0);
      Result := 0;
      Exit;
      end;
    end;
  finally
    CloseFile(f);
  end;
  { Create the POVRay input file, and add the definitions of the camera, the
    light sources and (optionally) of the coordinate system axes. }
  end else MessageDlg(ExtractFilePath(ParamStr(0)) + 'PovTreal.ini NOT found!', mtError, [mbOK], 0);

  try
    AssignFile(f, POVRayInputFileName);
    try
      Rewrite(f);
                  {Comment Copyright}
      Writeln(f, '// ' + LowerCase(ExtractFileName(POVRayInputFileName)) + ', generated by POVTReal on ' + DateToStr(Date));
      Writeln(f);

      { Write include statements }

      Writeln(f, '#include "colors.inc"');
      Writeln(f, '#include "tree.inc"');

      { Write camera definition }

      Writeln(f);
      Writeln(f, 'camera {');
      Writeln(f, '  location <' +
             FloatToStr(MaxY / 1.05) +
      ', ' + FloatToStr(MaxY / 2.1) +
      ', -' + FloatToStr(MaxY / 1.05) + '>');
      Writeln(f, '  look_at <0, ' + FloatToStr(MaxY / 2.1) + ', 0>');
      Writeln(f, '  right x*image_width/image_height');
      If ((ImageTotal>1)and (Animation=0)) then
                                                {360}
      Writeln(f, '  rotate <0,clock*'+inttostr(Rotation)+',0>');
      Writeln(f, '}');

      { Write area light definition }
       {These need comments...}        {1}
      If ((ImageTotal>1)and (Animation=2)) then
      begin
        Writeln(f);
        Writeln(f, 'light_source {');
        Writeln(f, '  <0, 0, 0>');
        Writeln(f, '  color rgb 1.0');
      Writeln(f, '  area_light');
      Writeln(f, '  <10, 0, 0> <0, 0, 10>');
      Writeln(f, '  4, 4');
      Writeln(f, '  adaptive 1');
      Writeln(f, '  circular');
      Writeln(f, '  orient');      
        {Writeln(f, '  translate <' +
         '40*sin(2*pi*clock),40*sin(2*pi*clock),-10*cos(2*pi*clock)>');}
      Writeln(f, '  translate <'+{40, }
      '40*sin('+floattostr(Rotation / 60)+'*clock),'+
      '40*sin('+floattostr(Rotation / 60)+'*clock),'+
      '-10*cos('+floattostr(Rotation / 60)+'*clock)>');
      {-10>');}
        Writeln(f, '}');
      end else
      begin
      Writeln(f);
      Writeln(f, 'light_source {');
      Writeln(f, '  0*x');
      Writeln(f, '  color rgb 1.0');
      Writeln(f, '  area_light');
      Writeln(f, '  <10, 0, 0> <0, 0, 10>');
      Writeln(f, '  4, 4');
      Writeln(f, '  adaptive 1');
      Writeln(f, '  jitter');    {Not in animations ?}
      Writeln(f, '  circular');
      Writeln(f, '  orient');
      Writeln(f, '  translate <40, 40, -10>');
      Writeln(f, '}');
      end;
      { Add a nice light blue background}
      (*BACKGROUND:
       background {COLOR}
        7.3.2 Color macros
        In POV-Ray all colors are handled in RGB color space
       RGB = < Red, Green, Blue, Filter, Transmit > *)

      Writeln(f);
      {ColorComboBox.ItemIndex
      White,LightBlue,CadetBlue,Cyan,Blue,Gray,Black}
      Case ImageBColor  of
      0: Writeln(f, 'background { color White }');
      1: Writeln(f, 'background { color LightBlue }');
      2: Writeln(f, 'background { color CadetBlue }');
      3: Writeln(f, 'background { color Cyan }');
      4: Writeln(f, 'background { color Blue }');
      5: Writeln(f, 'background { color Gray }');
      6: Writeln(f, 'background { color Black }');
      else Writeln(f, 'background { color LightBlue }');
      end;

      { In case the user wants to include the coordinate axes, include them }

      if IncludeAxes then
      begin
        Writeln(f);
        Writeln(f, '#declare Axes =');
        Writeln(f, 'union {');
        Writeln(f, '  // x-axis');
        Writeln(f);
        Writeln(f, '  cylinder { <0, 0, 0>, <1, 0, 0>, 0.01');
        Writeln(f, '    pigment { color Red }');
        Writeln(f, '  }');
        Writeln(f);
        Writeln(f, '  cone { <1, 0, 0>, 0.03, <1.1, 0, 0>, 0');
        Writeln(f, '    pigment { color Red }');
        Writeln(f, '  }');
        Writeln(f);
        Writeln(f, '  // y-axis');
        Writeln(f);
        Writeln(f, '  cylinder { <0, 0, 0>, <0, 1, 0>, 0.01');
        Writeln(f, '    pigment { color Blue }');
        Writeln(f, '  }');
        Writeln(f);
        Writeln(f, '  cone { <0, 1, 0>, 0.03, <0, 1.1, 0>, 0');
        Writeln(f, '    pigment { color Blue }');
        Writeln(f, '  }');
        Writeln(f);
        Writeln(f, '  // z-axis');
        Writeln(f);
        Writeln(f, '  cylinder { <0, 0, 0>, <0, 0, 1>, 0.01');
        Writeln(f, '    pigment { color Green }');
        Writeln(f, '  }');
        Writeln(f);
        Writeln(f, '  cone { <0, 0, 1>, 0.03, <0, 0, 1.1>, 0');
        Writeln(f, '    pigment { color Green }');
        Writeln(f, '  }');
        Writeln(f, '}');
        Writeln(f);
        Writeln(f, 'object { Axes');
        Writeln(f, '  scale ' + FloatToStr(MaxY / 2));
        Writeln(f, '}');
      end;

      { Include the tree object (defined in tree.inc) }

      Writeln(f);
      Writeln(f, 'object { Tree');
      If ((ImageTotal>1)and (Animation=1)) then
      Writeln(f, 'rotate '+inttostr(Rotation)+'*clock*y');
      Writeln(f, '}');
     except
      on EInOutError do
      begin
        MessageDlg('Error while writing to the POV-Ray input file!', mtError, [mbOK], 0);
        Result := 0;
        Exit;
      end;
    end;
  finally
    CloseFile(f);
  end;

  { Create the include file which contains the tree mesh definition }

  AssignFile(f, ExtractFilePath(POVRayInputFileName) + 'Tree.inc');
  try
    Rewrite(f);
    Writeln(f, '#include "colors.inc"');
    Writeln(f);
  except
    on EInOutError do
    begin
      MessageDlg('Error while writing to the POV-Ray input file!', mtError, [mbOK], 0);
      Result := 0;
      Exit;
    end;
  end;

  { We leave the file f open, because subsequent procedures will write to f
    again. Closing and appending to f for every procedure call of for example
    initializestem requires a lot of time, and virus scanners will scan the
    file 'tree.inc' like crazy, also consuming a lot of processor power. }

  Result := 1;
end;

function Finalize: integer; stdcall;
var ImageFormatChar: char;
    i: integer;
begin

  { Closing the tree.inc file }

  CloseFile(f);

  ProgressForm.Close;

  ImageFormatChar := ' ';

  case ImageFormat of
{    0: ImageFormatChar := 'C'; }{tga RLE compressed}
    0: ImageFormatChar := 'N'; {Uncompressed Targa-24  tga non compressed}
    1: ImageFormatChar := 'P'; {Png}
    2: ImageFormatChar := 'S';  {Unix PPM}
    3: ImageFormatChar := 'T';  {System specific}
  end;

  { Assemble all tree parts in one big tree object }

  try
    AssignFile(f, ExtractFilePath(POVRayInputFileName) + 'Tree.inc');
    try
      Append(f);
      Writeln(f);
      Writeln(f, '#declare Stems =');
      Writeln(f, 'union {');
      for i := 1 to TotalStems do Writeln(f, 'object { Stem' + IntToStr(i) + ' }');
      Writeln(f, 'texture { pigment { color DarkBrown }}');
      Writeln(f, '}');
      Writeln(f);
      Writeln(f, '#declare Leaves =');
      Writeln(f, 'union {');
      for i := 1 to TotalLeaves do Writeln(f, 'object { Leaf' + IntToStr(i) + ' }');
      Writeln(f, 'texture { pigment { color Green }}');
      Writeln(f, '}');
      Writeln(f);
      Writeln(f, '#declare Fruit =');
      Writeln(f, 'union {');
      for i := 1 to TotalFruit do Writeln(f, 'object { Fruit' + IntToStr(i) + ' }');
      Writeln(f, 'texture { pigment { color Red }}');
      Writeln(f, '}');
      Writeln(f);
      Writeln(f, '#declare Tree = ');
      Writeln(f, 'union {');
      Writeln(f, '  object { Stems');
      Writeln(f, '    finish { ambient 0.8 diffuse 0.8 }');
      Writeln(f, '  }');
      Writeln(f, '  object { Leaves');
      Writeln(f, '    finish { ambient 0.8 diffuse 0.8 }');
      Writeln(f, '  }');
      Writeln(f, '  object { Fruit');
      Writeln(f, '    finish { ambient 0.8 diffuse 0.8 }');
      Writeln(f, '  }');
      Writeln(f, '}');

    except
      on EInOutError do
      begin
        MessageDlg('Error while writing to the POV-Ray input file!', mtError, [mbOK], 0);
        Result := 0;
        Exit;
      end;
    end;
  finally
    CloseFile(f);
  end;

  { We create a temporary INI file to store all switches passed over to POVRay }

  try
    AssignFile(f, ExtractFilePath(POVRayInputFileName) + 'povtrealtmp.ini');
    try
      Rewrite(f);
      Writeln(f, 'Input_File_Name=' + POVRayInputFileName);
      Writeln(f, 'Output_File_Name=' + ImageFileName);
      Writeln(f, 'Width=' + IntToStr(ImageWidth));
      Writeln(f, 'Height=' + IntToStr(ImageHeight));
      Writeln(f, 'Output_File_Type=' + ImageFormatChar);
      { Write animation definition }
      { Rotation  Animation ImageTotal}
      if ImageTotal>1 then
      begin
        Writeln(f);
          {Wind ... ? External Rendered Animations...
           NOT the same Tree data
           Set a Boolean for Rendere to use to STEP through
           when it is re-rendering EACH frame prior to sending to POV-RAY}
        If Animation=0 then
        begin
          Writeln(f, 'Final_Frame='+inttostr(ImageTotal));
          {From POV Doc but not right ?.. image looks the same}
          Writeln(f, 'rotate '+inttostr(Rotation)+'*clock*y');
          {Camera and light rotated: }
          { Writeln(f, '  rotate <0,clock*'+inttostr(Rotation)+',0>');}
        end else
        begin
          Writeln(f, 'Final_Frame='+inttostr(ImageTotal));
          Writeln(f, 'rotate '+inttostr(Rotation)+'*clock*y');
        end;
      end;      
      Writeln(f, POVRayCommandLine);
    except
      on EInOutError do
      begin
        MessageDlg('Error while writing to the POV-Ray include file!', mtError, [mbOK], 0);
        Result := 0;
        Exit;
      end;
    end;
  finally
    CloseFile(f);
  end;

  POVRayCommandLine := '/render ' + 'povtrealtmp.ini';

  ShellExecute(
  Application.Handle,
  'open',
  PChar(POVRayExecutable),
  PChar(POVRayCommandLine),
  PChar(ExtractFilePath(POVRayInputFileName)),
  SW_SHOWNORMAL);

  Result := 1;
end;

function AddStem(Sections: TList): integer; stdcall;
{ Pre: Sections <> nil
  Ret: 0, AddStem failed
       1, AddStem succeeded, a mesh describing the stem segment made
          up by the sections defined in Sections has been written to 'tree.inc'. }
var i, j: integer;                           // Loop variables
    Vertices, Faces: integer;                // The amount of vertices and
                                             // faces respectively in the
                                             // segment mesh
    VerticesPerSection: integer;             // The amount of vertices in a
                                             // section (is the same for all
                                             // sections)
    TotalSections: integer;                  // Total amount of sections
begin
  TotalStems := TotalStems + 1;

  { Count the amount of sections }

  TotalSections := Sections.Count;

  { Count the amount of vertices in the stem }

  VerticesPerSection := TList(Sections[0]).Count;

  Vertices := VerticesPerSection * TotalSections;

  { Update the total amount of vertices in the tree }

  TotalVertices := TotalVertices + Vertices;

  { Calculate the total amount of faces in the stem }

  Faces := 2 * Vertices - 2 * VerticesPerSection;

  try
    Writeln(f);
    Writeln(f, '#declare Stem' + IntToStr(TotalStems) + ' =');
    Writeln(f, 'mesh2 {');

    { See for more information about the structure of the mesh2 object the POV-Ray
      online help }


    { Write vertices making up the mesh }

    Writeln(f, 'vertex_vectors {');
    Write(f, IntToStr(Vertices) + ',');

    { For every section making up the stem write the vertices from that section
      to the vertex list of the mesh2 object }

    for i := 0 to TotalSections - 1 do
    begin

      for j := 0 to VerticesPerSection - 1 do
      begin

        { Repeatedly write a vertex from the current section }

        Write(f, '<' + FloatToStrF(TPoint3d(TList(Sections[i])[j]).x, ffGeneral, 5, 4) + ',' +
                       FloatToStrF(TPoint3d(TList(Sections[i])[j]).y, ffGeneral, 5, 4) + ',' +
                       FloatToStrF(TPoint3d(TList(Sections[i])[j]).z, ffGeneral, 5, 4) + '>');

        Write(f, ',');

      end;

    end;

    Writeln(f, '}');

    { Write the face indices }

    Writeln(f, 'face_indices {');
    Write(f, IntToStr(Faces) + ',');

    { For every section write the segment mesh. The outer loop has a maximum of
      TotalSections - 2 because the faces are built using points from section i
      and section i + 1}

    for i := 0 to TotalSections - 2 do
    begin

      for j := 0 to VerticesPerSection - 1 do
      begin

        { Repeatedly write two face indices creating a cubic little plane between
          four vertices }

        { VerticesPerSection * i + j: lower right corner of the first face
          VerticesPerSection * (i + 1) + j: upper right corner of the first face
          VerticesPerSection * i + (j + 1) mod VerticesPerSection: lower left corner of the first face

          VerticesPerSection * i + (j + 1) mod VerticesPerSection: lower left corner of the second face
          VerticesPerSection * (i + 1) + j: upper right corner of the second face
          VerticesPerSection * (i + 1) + (j + 1) mod VerticesPerSection: upper left corner of the second face }


        Write(f, '<' + IntToStr(VerticesPerSection * i + j) + ',' +
                       IntToStr(VerticesPerSection * (i + 1) + j) + ',' +
                       IntToStr(VerticesPerSection * i + (j + 1) mod VerticesPerSection) + '>,<' +
                       IntToStr(VerticesPerSection * i + (j + 1) mod VerticesPerSection) + ',' +
                       IntToStr(VerticesPerSection * (i + 1) + j) + ',' +
                       IntToStr(VerticesPerSection * (i + 1) + (j + 1) mod VerticesPerSection) + '>');

        Write(f, ',');

      end;
    end;

    Writeln(f, '}');
    Writeln(f, '}');

  except
    on EInOutError do
    begin
      MessageDlg('IO error while writing to the POV-Ray include file!', mtError, [mbOK], 0);
      Result := 0;
      Exit;
    end;
  end;
  ProgressForm.ProgressBar.StepIt;
  Result := 1;
end;


function AddLeaf(Points: TList; LeafColor: integer; LeafColorVariation: boolean): integer; stdcall;
var i: integer;
    Sign: integer;
    Red, Blue, Green: integer;
    Variation: integer;
begin
  TotalLeaves := TotalLeaves + 1;
  try
    Writeln(f);
    Writeln(f, '#declare Leaf' + IntToStr(TotalLeaves) + ' =');
    Writeln(f, 'polygon {');
    Write(f, IntToStr(Points.Count) + ',');

    for i := 0 to Points.Count - 1 do
    begin
      Write(f, '<' + FloatToStrF(TPoint3d(Points[i]).x, ffGeneral, 15, 4) + ',' +
                     FloatToStrF(TPoint3d(Points[i]).y, ffGeneral, 15, 4) + ',' +
                     FloatToStrF(TPoint3d(Points[i]).z, ffGeneral, 15, 4) + '>');
      if i <> Points.Count - 1 then Write(f, ',');
    end;

    { Calculate the red, green and blue components from the leafcolor variable }

    Red := LeafColor and $FF;
    Green := (LeafColor and $FF00) shr 8;
    Blue := (LeafColor and $FF0000) shr 16;

    if LeafColorVariation then
    begin

      { Add a small variation to the leaf color }

      if Random(2) = 0 then Sign := 1 else Sign := -1;

      Variation := Random(40);

      Red := Red + Sign * Variation;
      Green := Green + Sign * Variation;
      Blue := Blue + Sign * Variation;

      if Red < 0 then Red := 0;
      if Green < 0 then Green := 0;
      if Blue < 0 then Blue := 0;
    end;

    Writeln(f, 'pigment { color rgb <' + FloatToStr(Red / $FF) + ',' +
                                         FloatToStr(Green / $FF) + ',' +
                                         FloatToStr(Blue / $FF) + '> }');
    Writeln(f, '}');
  except
    on EInOutError do
    begin
      MessageDlg('IO error while writing to the POV-Ray include file!', mtError, [mbOK], 0);
      Result := 0;
      Exit;
    end;
  end;
  Result := 1;
end;


function AddFruit(Points: TList; FruitColor: integer; FruitColorVariation: boolean): integer; stdcall;
var i: integer;
    Sign: integer;
    Red, Blue, Green: integer;
    Variation: integer;
begin
  TotalFruit := TotalFruit + 1;
  try
    Writeln(f);
    Writeln(f, '#declare Fruit' + IntToStr(TotalFruit) + ' =');
    Writeln(f, 'polygon {');
    Write(f, IntToStr(Points.Count) + ',');

    for i := 0 to Points.Count - 1 do
    begin
      Write(f, '<' + FloatToStrF(TPoint3d(Points[i]).x, ffGeneral, 15, 4) + ',' +
                     FloatToStrF(TPoint3d(Points[i]).y, ffGeneral, 15, 4) + ',' +
                     FloatToStrF(TPoint3d(Points[i]).z, ffGeneral, 15, 4) + '>');
      if i <> Points.Count - 1 then Write(f, ',');
    end;

    { Calculate the red, green and blue components from the leafcolor variable }

    Red := FruitColor and $FF;
    Green := (FruitColor and $FF00) shr 8;
    Blue := (FruitColor and $FF0000) shr 16;

    if FruitColorVariation then
    begin

      { Add a small variation to the leaf color }

      if Random(2) = 0 then Sign := 1 else Sign := -1;

      Variation := Random(40);

      Red := Red + Sign * Variation;
      Green := Green + Sign * Variation;
      Blue := Blue + Sign * Variation;

      if Red < 0 then Red := 0;
      if Green < 0 then Green := 0;
      if Blue < 0 then Blue := 0;
    end;

    Writeln(f, 'pigment { color rgb <' + FloatToStr(Red / $FF) + ',' +
                                         FloatToStr(Green / $FF) + ',' +
                                         FloatToStr(Blue / $FF) + '> }');
    Writeln(f, '}');
  except
    on EInOutError do
    begin
      MessageDlg('IO error while writing to the POV-Ray include file!', mtError, [mbOK], 0);
      Result := 0;
      Exit;
    end;
  end;
  Result := 1;
end;

exports
  ShowForm, Initialize, Finalize, AddStem, AddLeaf, AddFruit;

begin
end.
